---
title: Run lifecycle
summary: Execution flow from the entry point through Initialize, Execute, and Finish for OnBaseKeywordSync.
status: draft
owner: Documents
last_reviewed: 2026-09-17
---

# Run lifecycle

`Main` constructs `Program` and calls `Run()`. Because `ArgumentContainer` declares no properties, argument resolution always succeeds and the application always reaches `Execute()`, which performs the entire keyword-sync operation before `Finish()` runs as a no-op.

## Execution flow

```mermaid
flowchart TD
    MAIN[Main] --> RUN["Program.Run()"]
    RUN --> ARGS["CreateArgumentContainer(args)<br>new ArgumentContainer(args)"]
    ARGS --> RESOLVE["Arguments.ResolveArguments()<br>always succeeds - no arguments declared"]
    RESOLVE -->|Invalid - unreachable here| FAIL["Write failures to Console.Error<br>ExitCode = 1<br>application never runs"]
    RESOLVE -->|Valid| REG["RegisterAppDependencies(builder)"]
    REG --> BUILD["Build container + CreateApplication(context)<br>new OnBaseKeywordSyncRunner(...)"]
    BUILD --> INIT["Initialize()<br>no-op"]
    INIT --> ZERO["ExitCode = 0"]
    ZERO --> EXEC["Execute()<br>keyword sync operation"]
    EXEC --> FIN["Finish()<br>no-op"]
    FIN --> DISPOSE["Dispose() + WriteFooter()"]
    DISPOSE --> ECHO["Print 'Exit Code: n'"]
    EXEC -.->|Unhandled Exception| ERR5["ExitCode = 5"]
```

## Argument resolution

`ArgumentContainer` declares no properties, so `BuildArgumentResolvers` adds nothing beyond the inherited base call. `ResolveArguments()` therefore always succeeds, and the exit-code-1 argument-validation path is unreachable for this application*. See [Arguments](arguments.md) for the complete (empty) contract.

## Dependency registration

`RegisterAppDependencies` calls `builder.RegisterCommonDependencies(Environment.UserName, new[] { typeof(Program).Assembly })` with no `DatabaseConfig` arguments, so no unit-of-work or database connection is ever registered. It also registers `SAIF.Services.LogService` as `ILogService` (never resolved) and calls `builder.ConfigureOpenTelemetry(options => { options.Owner = "Documents"; })`. See [Architecture](architecture.md) and [Dependencies](dependencies.md) for the full registration table.

## Initialize

`OnBaseKeywordSyncRunner.Initialize()` has an empty body — no resource acquisition or preflight checks. The Imaging API client is instead constructed inside `Execute()`.

| Step | Action | On failure |
| --- | --- | --- |
| 1 | No-op | — |

## Execute

The entire business operation runs in `Execute()`. A one-paragraph summary follows; the step-by-step logic is documented per operation under [Operations and processes](operations-processes.md).

### Processing pattern

| Step | Action | On success | On failure |
| --- | --- | --- | --- |
| 1 | Log start, read `OnBaseKeywordSync.onbaseasync`, construct `ImagingApiClient` | Continue to clearing invalid person syncs | Exception → `LogError`/`LogException` → rethrow → exit `5` |
| 2 | Clear invalid person keyword syncs, then retrieve and order unprocessed keyword syncs | Continue to sync | Exception → rethrow → exit `5` |
| 3 | Sync the batch with OnBase (per-record decision, send, mark processed) | Log per-record output, batch completes | Any record's failure aborts the remaining batch — no per-record isolation* |

## Finish

`OnBaseKeywordSyncRunner.Finish()` has an empty body — no explicit teardown, totals, or notification is emitted. This is a finding, not an omission.

## Exit codes

Exit codes are inherited from the `SaifConsoleException` hierarchy. The table below records which conditions in **this application** produce each code.

| Exit code | Exception | Triggered by |
| --- | --- | --- |
| 0 | — | `Execute()` and `Finish()` complete without throwing |
| 1 | Argument validation / `BadParameterException` | Not used — no arguments are declared and no `BadParameterException` is thrown* |
| 2 | `DatabaseErrorException` | Not used — no database access exists |
| 3 | `DataErrorException` | Not used |
| 4 | `FileInputOutputException` | Not used |
| 5 | Unhandled exception | Any exception rethrown from `Execute()` — Imaging API HTTP failure, OnBase WCF fault/timeout, missing `customquerymappings` entry, `MarkProcessed` failure, or a configuration key that fails to convert |
| 6 | `NoDataFoundException` | Not used — an empty unprocessed set simply logs a zero-record summary and exits `0` |
| *n/a* | `CustomErrorException` | Not used — no `CustomErrorException` is constructed anywhere |

In practice the observable contract is binary: `0` on success, `5` on any failure.

## Error handling

### Recoverable errors
None. There is no per-record error isolation: one failed record aborts the remaining batch, and already-processed records are not rolled back*.

### Fatal errors
`OnBaseKeywordSyncRunner.Execute` wraps the whole operation in a `try`/`catch (Exception ex)` that logs the message, calls `LogService.LogException(ex)`, then rethrows. `ProgramRunnerBase.Run` catches the rethrown exception; since the application never throws a `SaifConsoleException` subclass, every failure maps to exit code `5`†.

## Related documentation
- [Application index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Arguments](arguments.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with `†` are derived from dependency library documentation via the Legends CLI. Statements marked with `*` were inferred from code structure or contextual evidence.

## Sources
- `OnBaseKeywordSync/Program.cs`
- `OnBaseKeywordSync/OnBaseKeywordSyncRunner.cs`
- `OnBaseKeywordSync/Services/ImagingApiClient.cs`
- `OnBaseKeywordSync/Services/KeywordUpdater.cs`
